import {
  compose, toUpper,
  last, map, head, split,
  intercalate, reverse, curry,
  toLower, replace, prop, append,
  match, add, Maybe, safeHead, maybe,
  Right, Either, left
} from '@utils/functions';

class Flock {
  public seagulls: number;

  constructor(seagulls) {
    this.seagulls = seagulls;
  }

  public conjoin(other: Flock): Flock {
    this.seagulls += other.seagulls;
    return this;
  }

  public breed(other: Flock): Flock {
    this.seagulls *= other.seagulls;
    return this;
  }
}

const flock_a = new Flock(4);
const flock_b = new Flock(2);
const flock_c = new Flock(0);

// (a + c) * b + a *b
// console.log(flock_a.conjoin(flock_c).breed(flock_b).conjoin(flock_a.breed(flock_b)).seagulls);
// add(multiply(add(flock_a.seagulls, flock_c.seagulls), flock_b.seagulls),
// multiply(flock_a.seagulls, flock_b.seagulls));
// multiply(flock_b.seagulls, add(flock_a.seagulls, flock_a.seagulls));

const increment = add(1);
const addTen = add(10);

const exclaim = x => `${x}!`;
console.log(increment(2), addTen(2));

const lastUpper = compose(toUpper, last);
const angry = compose(exclaim, toUpper);

console.log(last(['jump-kick', 'round-house', 'uppercut']));
console.log(lastUpper(['jump-kick', 'round-house', 'uppercut']));

const name = "hunter stockton thompson";
const initials = compose(intercalate('. '), map(compose(toUpper, head)), split(' '));
console.log(initials(name));

const latin = compose(map(angry), reverse);
console.log(latin(['frog', 'eyes']));

const trace = curry((tag, x) => {
  console.log(tag, x);
  return x;
});

const dasherize = compose(intercalate('-'), map(toLower), trace('after split'), split(' '), replace(/\s{2,}/ig, ' '));
console.log(dasherize('The world is a vampire'));

// A Flickr of Functional Programming

import $ from 'jquery';

function FlickrApp() {
  const Impure = {
    // tslint:disable-next-line:no-shadowed-variable
    getJSON: curry((callback, url) => $.getJSON(url, callback)),
    setHtml: curry((sel, html) => $(sel).html(html)),
  };

  const host = 'api.flickr.com';
  const path = '/services/feeds/photos_public.gne';
  const query = t => `?tags=${t}&format=json&jsoncallback=?`;

  const url = t => `https://${host}${path}${query(t)}`;

  const img = src => $('<img/>', { src, alt: '' });

  /*
  const mediaUrl = compose(prop('m'), prop('media'));
  const mediaUrls = compose(map(mediaUrl), prop('items'));
  const images = compose(map(img), mediaUrls);
   */

  const mediaUrl = compose(prop('m'), prop('media'));
  const media2Img = compose(img, mediaUrl);
  const images = compose(map(media2Img), prop('items'));

  const render = compose(Impure.setHtml('#js-main'), images);
  const app = compose(Impure.getJSON(render), url);

  app('pig');
}

class Container<T> {
  private readonly $value: T;

  constructor(x: T) {
    this.$value = x;
  }

  public static of<T>(x: T) {
    return new Container(x);
  }

  public map(fn: (x: T) => T) {
    return Container.of(fn(this.$value));
  }
}

console.log(Container.of(1));
console.log(Container.of(2).map(v => v * 2));
console.log(
  Container
    .of('bombs')
    .map(append(' away'))
    .map(prop('length'))
);

console.log(Maybe.of('Malkovich Malkovich').map(match(/a/ig)));
console.log(
  Maybe.of(null).map(match(/a/ig))
);

console.log(
  Maybe.of({ name: 'Boris' }).map(prop('age')).map(add(10))
);

console.log(
  Maybe.of({ name: 'Dinah', age: 14 }).map(prop('age')).map(add(10))
);

const streetName = compose(map(prop('street')), safeHead, prop('addresses'));
console.log(streetName({ addresses: [] }));
console.log(
  streetName({ addresses: [{ street: 'Shady Ln.', number: 4201 }] })
);

const withdraw = curry((amount, { balance }) => Maybe.of(balance >= amount ? { balance: balance - amount } : null));
const updateLedger = account => account;
const remainingBalance = ({ balance }) => `Your balance is $${balance}`;
const finishTransaction = compose(remainingBalance, updateLedger);
const getTwenty = compose(maybe('You\'re broke!', finishTransaction), withdraw(20));
console.log(getTwenty({ balance: 200.00 }));
console.log(getTwenty({ balance: 10.00 }));

import moment from "moment";

const getAge = curry((now, user) => {
  const birthDate = moment(user.birthDate, 'YYYY-MM-DD');
  return birthDate.isValid()
    ? Either.of(now.diff(birthDate, 'years'))
    : left('Birth date could not be parsed');
});

console.log(161, getAge(moment(), { birthDate: '2005-12-12' }));
console.log(162, getAge(moment(), { birthDate: 'July 4, 2001' }));

const fortune = compose(append('If you survive, you will be '), toString, add(1));
const zoltar = compose(map(console.log), map(fortune), getAge(moment()));
zoltar({ birthDate: '2005-12-12' });
zoltar({ birthDate: 'balloons' });
